#include "EventLoop.h"
#include "Channel.h"
#include "Poller.h"
#include <sys/eventfd.h>
#include <unistd.h>
#include <fcntl.h>
#include <memory>
#include "Logger.h"
namespace mymuduo {

// 防止一个线程创建多个 EventLoop
__thread EventLoop* t_loopInThisThread = nullptr;

// 定义默认 Poller 的 I/O 复用接口超时时间
const int kPollTimeMs = 10000;

// 创建 wakeupfd，用来唤醒 subReactor 处理新来的 channel
int createEventfd() {
    int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    if(evtfd < 0){
        LOG_FATAL("func=%s => eventfd error:%d\n", __FUNCTION__, errno);
    }
    return evtfd;
}

EventLoop::EventLoop() 
    : looping_(false)
    , quit_(false)
    , callingPendingFunctors_(false)
    , threadId_(currentthread::tid())
    , poller_(Poller::newDefaultPoller(this))
    , wakeupFd_(createEventfd())
    , wakeupChannel_(new Channel(this, wakeupFd_)) {

    LOG_DEBUG("EventLoop created %p in thread%d\n", this, threadId_);
    if(t_loopInThisThread) {
        LOG_FATAL("Another EventLoop %p exists in this thread %d\n", t_loopInThisThread, threadId_);
    } else {
        t_loopInThisThread = this;
    }

    // 设置 wakeupfd 的事件类型以及发生事件后的回调操作
    wakeupChannel_->setReadCallback(std::bind(&EventLoop::handleRead, this));
    // 每一个 eventloop 都监听 wakeupchannel 的 EPOLLIN 读事件
    wakeupChannel_->enableReading();
}

EventLoop::~EventLoop() {
    wakeupChannel_->disableAll();
    wakeupChannel_->remove();
    ::close(wakeupFd_);
    t_loopInThisThread = nullptr;
}

void EventLoop::loop() {
    looping_ = true;
    quit_ = false;
    LOG_INFO("EventLoop %p start looping\n", this);
    while(!quit_) {
        activeChannels_.clear();
        // 监听两类 fd，一种是 client 的 fd，一种是 wakeupfd
        pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
        for(Channel* channel : activeChannels_) {
            // Poller 监听哪些 channel 发生了事件，然后上报给 EventLoop，然后通知 channel 处理相应的事件
            channel->handleEvent(pollReturnTime_);
        }
        // 执行当前 EventLoop 事件循环需要处理的回调操作
        /* 
         * I/O 线程(mainloop) 接受新用户的连接  channel 打包 fd，已连接的用户分发给 subloop
         * mainloop 实现注册一个回调（需要 subloop 执行）wakeup subloop后，执行之前 mainloop 注册的回调
         */
        doPendingFunctors();
    }
    LOG_INFO("EventLoop %p stop looping\n", this);
    looping_ = false;
}

void EventLoop::quit() {
    quit_ = true;
    // 1. loop 在自己的线程中调用 quit，那就无所谓
    // 2. 但是如果是在其他线程调用 quit，应该先将该线程唤醒，让他不再卡在 poller_->poll，能够向下执行循环
    if(!isInLoopThread()) {
        wakeup();
    }
}

void EventLoop::runInLoop(Functor cb) {
    
    if(isInLoopThread()) {
        // 在当前的 loop 线程中执行 cb
        cb();
    } else {
        // 不在当前的 loop 线程中执行 cb，唤醒 loop
        queueInLoop(cb);
    }
}

void EventLoop::queueInLoop(Functor cb) {
    {
        std::unique_lock<std::mutex> lock(mutex_);
        pendingFunctors_.emplace_back(cb);
    }
    // 唤醒相应的，需要执行上面回调操作的 loop 的线程了
    if(!isInLoopThread() || callingPendingFunctors_) {
        // || callingPendingFunctors_ 意思是当前 loop 正在执行回调，但是 loop 又有了新的回调
        wakeup(); // 唤醒 loop 所在线程
    }
}

void EventLoop::handleRead()
{
  uint64_t one = 1;
  ssize_t n = read(wakeupFd_, &one, sizeof(one));
  if (n != sizeof(one)) {
    LOG_ERROR("EventLoop::handleRead() reads %ld bytes instead of 8\n", n);
  }
}

void EventLoop::wakeup() {
    // 唤醒 loop 所在线程，向 wakeupFd_ 写一个数据，wakeupChannel 就发生读事件，当前 loop 就唤醒
    uint64_t one = 1;
    ssize_t n = write(wakeupFd_, &one, sizeof(one));
    if(n != sizeof(one)) {
        LOG_ERROR("EventLoop::wakeup() writes %lu bytes instead of 8\n", n);
    }
}

void EventLoop::updateChannel(Channel* channel) {
    poller_->updateChannel(channel);
}

void EventLoop::removeChannel(Channel* channel) {
    poller_->removeChannel(channel);
}

bool EventLoop::hasChannel(Channel* channel) {
    return poller_->hasChannel(channel);
}

void EventLoop::doPendingFunctors() {
    std::vector<Functor> functors;
    callingPendingFunctors_ = true;
    {
        std::unique_lock<std::mutex> lock(mutex_);
        functors.swap(pendingFunctors_);
    }
    for(const Functor functor : functors) {
        functor(); // 执行当前 loop 需要执行的回调操作
    }
    callingPendingFunctors_ = false;
}

}
